﻿using System.IO;
using System.Text.RegularExpressions;
using Framework.Core.Dependency.Domain;

namespace Framework.Core.Dependency.Parser
{
    internal class SolutionParser : IStreamParser<Solution>
    {
        private static readonly Regex SlnRegex =
            new Regex(@"Project\(\""[^\""]*\""\)\s=\s\""(?<Name>[^\""]*)\"",\s\""(?<Project>[^\""]*)\""",
                      RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

        private readonly IStreamParser<Project> cppProjectParser;
        private readonly IStreamParser<Project> dotNetProjectParser;

        public SolutionParser()
            : this(new DotNetProjectParser(), new CppProjectParser())
        {
        }

        private SolutionParser(IStreamParser<Project> dotNetProjectParser, IStreamParser<Project> cppProjectParser)
        {
            this.dotNetProjectParser = dotNetProjectParser;
            this.cppProjectParser = cppProjectParser;
        }

        public Solution Parse(Stream stream, string file, string branch)
        {
            using (var reader = new StreamReader(stream))
            {
                var slnContent = reader.ReadToEnd();

                var solution = new Solution(Path.GetFileNameWithoutExtension(file),
                                            Path.GetExtension(file),
                                            file,
                                            Language.CSharp,
                                            branch);

                var match = SlnRegex.Match(slnContent);

                while (match.Success)
                {
                    var projectCapture = match.Groups["Project"].Value;
                    var projectPath = GetProjectPath(file, projectCapture);

                    if (!File.Exists(projectPath))
                    {
                        match = match.NextMatch();
                        continue;
                    }

                    if (IsCppProject(projectPath))
                    {
                        using (var projectStream = File.OpenRead(projectPath))
                        {
                            solution.AddProject(cppProjectParser.Parse(projectStream, projectPath, branch), branch);
                        }
                    }

                    if (IsDotNetProject(projectPath))
                    {
                        using (var projectStream = File.OpenRead(projectPath))
                        {
                            solution.AddProject(dotNetProjectParser.Parse(projectStream, projectPath, branch), branch);
                        }
                    }
                    match = match.NextMatch();
                }

                return solution;
            }
        }

        private static bool IsCppProject(string projectPath)
        {
            return projectPath.EndsWith(".vcxproj") || projectPath.EndsWith(".vcproj");
        }

        private static bool IsDotNetProject(string projectPath)
        {
            return projectPath.EndsWith(".csproj") || projectPath.EndsWith(".vbproj");
        }

        private static string GetProjectPath(string slnPath, string relativeProjectPath)
        {
            const string httpLocalhost = "http://localhost/";
            if (relativeProjectPath.StartsWith(httpLocalhost))
            {
                relativeProjectPath = relativeProjectPath.Replace(httpLocalhost, string.Empty);
            }
            // ReSharper disable PossibleNullReferenceException
            return Path.Combine(new FileInfo(slnPath).Directory.FullName, relativeProjectPath);
            // ReSharper restore PossibleNullReferenceException
        }
    }
}